﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.model;
using snat.algorithms;
using algorithms.Algorithms.Utils.ResultSets;

namespace algorithms.algorithms.SimpleLinkPrediction
{
    public class LinkPredictionVerifier<T>  : IAlgorithm, ITextRenderable
    {
        public int WindowLength{get; private set;}
        private LocalLinkPrediction<T> llp;
        private int winPos = 0;
        private List<KeyValuePair<BaseNode, BaseNode>> ranking;

        public LinkPredictionVerifier(Func<BaseNode, BaseNode, T> _scoringFunc, int _windowLength) {
            WindowLength = _windowLength;
            llp = new LocalLinkPrediction<T>(_scoringFunc);
        }


        public List<ROCDataPoint> ROCCurve {
            get;
            private set;
        }




        #region IAlgorithm Members

        public string Name {
            get {
                return "ROC verifier of " + llp.Name;
            }
        }

        public void Register() {
            NetworkModel.Instance.MessageProcessed += Instance_MessageProcessed;
            NetworkModel.Instance.TapeEnded += DoValidation;
            llp.Register();
        }

        bool fired = false;
        void Instance_MessageProcessed(Message m) {
            winPos++;
            if (winPos % WindowLength == 0 && !fired) {
                llp.Process();
                ranking = llp.LastRanking;
                ResettableNode.TriggerOverlayReset();
                fired = true;
            }
        }

        private void DoValidation() {
            if (fired == false) {
                llp.Process();
                ranking = llp.LastRanking;
            }

            ROCCurve = new List<ROCDataPoint>();
            int truePos, falsePos, trueNeg, falseNeg;
            for (double belief = 0; belief <= 1; belief += 0.005) {
                int posPredictions = (int) ((double) ranking.Count * belief);
                int negPredictions = ranking.Count - posPredictions;
                truePos = falsePos = trueNeg = falseNeg = 0;

                for (int prediction = 0; prediction < posPredictions; prediction++) {
                    KeyValuePair<BaseNode, BaseNode> predictedLink = ranking[prediction];
                    if (predictedLink.Key.OverlayOutEdges.Exists(e => e.Target == predictedLink.Value)) {
                        truePos++;
                    }
                    else {
                        falsePos++;
                    }
                }
                for (int prediction = posPredictions; prediction < ranking.Count; prediction++) {
                    KeyValuePair<BaseNode, BaseNode> predictedLink = ranking[prediction];
                    if (predictedLink.Key.OverlayOutEdges.Exists(e => e.Target == predictedLink.Value)) {
                        falseNeg++;
                    }
                    else {
                        trueNeg++;
                    }
                }
                ROCCurve.Add(new ROCDataPoint(truePos, falsePos, trueNeg, falseNeg));
            }
            if (RenderText != null) RenderText(this);
        }

        public void DeRegister() {
            NetworkModel.Instance.MessageProcessed -= Instance_MessageProcessed;
            NetworkModel.Instance.TapeEnded -= DoValidation;
            llp.DeRegister();
        }

        #endregion

        #region ITextRenderable Members

        public event RenderTextDelegate RenderText;

        public string GetRenderableText() {
            StringBuilder ret = new StringBuilder();
            foreach (ROCDataPoint rdp in ROCCurve) {
                ret.AppendFormat("{0}, {1}\n", rdp.TruePositiveRate, rdp.FalsePositiveRate);
            }
            return ret.ToString();
            
        }

        #endregion
    }
}
